package com.eureka.asyn.service.impl;

import com.eureka.asyn.entity.BaseFileEntity;
import com.eureka.asyn.service.FileService;
import com.eureka.asyn.util.OssUtil;
import com.eureka.bclass.base.BaseException;
import com.eureka.bclass.base.BaseResult;
import com.eureka.bclass.base.BaseUploadResult;
import com.eureka.bclass.enums.ProfilesActiveEnum;
import com.eureka.bclass.enums.UploadFileTypeEnum;
import com.eureka.bclass.util.DateUtil;
import com.eureka.bclass.util.FileUtil;
import com.eureka.bclass.util.LoggerUtil;
import com.eureka.bclass.util.StrUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
 * @Description: 文件接口实现类
 * @Author: lixing
 * @Date: 2021/6/6 12:31
 */
@Service
public class FileServiceImpl implements FileService {
    /** 环境变量 */
    @Value("${spring.profiles.active}")
    private String profilesActive;

    /** 外网访问的绝对路径 */
    @Value("${upload.domain}")
    private String uploadDomain;
    /** 存储在服务器上的绝对路径 */
    @Value("${upload.path}")
    private String uploadPath;

    /**
     * 图片上传
     */
    @Value("${upload.image.path}")
    private String imagePath;
    @Value("${upload.image.format}")
    private String imageFormat;
    @Value("${upload.image.size}")
    private long imageSize;

    /**
     * 文本上传
     */
    @Value("${upload.text.path}")
    private String textPath;
    @Value("${upload.text.format}")
    private String textFormat;
    @Value("${upload.text.size}")
    private String textSize;

    /**
     * 视频上传
     */
    @Value("${upload.video.path}")
    private String videoPath;
    @Value("${upload.video.format}")
    private String videoFormat;
    @Value("${upload.video.size}")
    private String videoSize;

    /**
     * excel上传
     */
    @Value("${upload.excel.path}")
    private String excelPath;
    @Value("${upload.excel.format}")
    private String excelFormat;
    @Value("${upload.excel.size}")
    private String excelSize;

    /**
     * word上传
     */
    @Value("${upload.word.path}")
    private String wordPath;
    @Value("${upload.word.format}")
    private String wordFormat;
    @Value("${upload.word.size}")
    private String wordSize;

    /**
     * pdf上传
     */
    @Value("${upload.pdf.path}")
    private String pdfPath;
    @Value("${upload.pdf.format}")
    private String pdfFormat;
    @Value("${upload.pdf.size}")
    private String pdfSize;

    /**
     * 阿里云 OSS 云文件存储相关配置
     */
    @Value("${aliyun.accessKeyId}")
    private String accessKeyId;
    @Value("${aliyun.accessKeySecret}")
    private String accessKeySecret;
    @Value("${aliyun.oss.bucketName}")
    private String bucketName;
    @Value("${aliyun.oss.endpoint}")
    private String endpoint;
    @Value("${aliyun.oss.fileUrlPrefix}")
    private String fileUrlPrefix;

    /**
     * @Description 校验文件上传的类型,并生成绝对路径
     * @Author lixing
     * @Date 2021/6/14 20:55
     */
    private BaseResult<String> checkUploadFile(BaseFileEntity baseEntity) {
        MultipartFile[] multipartFiles = baseEntity.getBaseUploadMultipartFiles();
        MultipartFile multipartFile = null;
        if (multipartFiles.length == 0 || multipartFiles[0].isEmpty()) {
            return new BaseResult<String>().fail("文件不存在，请重新上传");
        }
        multipartFile = multipartFiles[0];
        // 上传文件的格式
        String contentType = multipartFile.getContentType();
        if (!StrUtil.isEffective(contentType) || null == contentType) {
            return new BaseResult<String>().fail("文件格式非法");
        }
        // 上传文件的类型。image,text,video,excel,word,pdf
        String baseUploadFileType = baseEntity.getBaseUploadFileType();
        if (!StrUtil.isEffective(baseUploadFileType)) {
            return new BaseResult<String>().fail("文件类型标识缺失");
        }
        // 上传文件的业务场景。例如:cover(头像图片上传)
        String baseUploadCategory = baseEntity.getBaseUploadCategory();
        if (!StrUtil.isEffective(baseUploadCategory)) {
            return new BaseResult<String>().fail("文件所属业务场景标识缺失");
        }
        // 默认读取的单位为：字节（B、KB、MB、GB、TB）
        long fileSize = multipartFile.getSize();
        if (UploadFileTypeEnum.IMAGE.getType().equals(baseUploadFileType)) {
            // TODO 图片
            contentType = contentType.replace("image/", "");
            if (!imageFormat.contains(contentType)) {
                return new BaseResult<String>().fail("请上传jpg、png、jpeg格式的图片");
            }
            if (fileSize > imageSize) {
                return new BaseResult<String>().fail(MessageFormat.format("图片文件不能超过{0}MB", imageSize / 1024 / 1024));
            }
            String relativePath = MessageFormat.format("{0}{1}/{2}/{3}", uploadPath, imagePath, baseUploadCategory, DateUtil.dateToDateCharacter(new Date()));
            FileUtil.createDirectory(relativePath);
            return new BaseResult<String>().success(relativePath);
        } else if (UploadFileTypeEnum.TXT.getType().equals(baseUploadFileType)) {
            // TODO 文本
            return new BaseResult<String>().fail("上传文件格式非法");
        } else if (UploadFileTypeEnum.VIDEO.getType().equals(baseUploadFileType)) {
            // TODO 视频
            return new BaseResult<String>().fail("上传文件格式非法");
        } else if (UploadFileTypeEnum.EXCEL.getType().equals(baseUploadFileType)) {
            // TODO EXCEL
            return new BaseResult<String>().fail("上传文件格式非法");
        } else if (UploadFileTypeEnum.WORD.getType().equals(baseUploadFileType)) {
            // TODO WORD
            return new BaseResult<String>().fail("上传文件格式非法");
        } else if (UploadFileTypeEnum.PDF.getType().equals(baseUploadFileType)) {
            // TODO PDF
            return new BaseResult<String>().fail("上传文件格式非法");
        } else {
            // TODO 其他
            return new BaseResult<String>().fail("上传文件格式非法");
        }
    }

    /**
     * @Description 文件上传
     * @Author lixing
     * @Date 2021/6/14 20:55
     */
    @Override
    public CompletableFuture<BaseResult<BaseUploadResult>> upload(BaseFileEntity baseEntity) {
        // 自定义校验规则
        BaseResult<String> checkUploadFile = checkUploadFile(baseEntity);
        if (checkUploadFile.getCode() == 1) {
            return CompletableFuture.completedFuture(new BaseResult<BaseUploadResult>().fail(checkUploadFile.getMsg()));
        }
        // 上传文件的绝对路径
        String pathStr = checkUploadFile.getData();
        List<String> filePathList = new ArrayList<>();
        try {
            MultipartFile[] multipartFiles = baseEntity.getBaseUploadMultipartFiles();
            for (MultipartFile multipartFile : multipartFiles) {
                // 重置文件名称 // 原始文件名，格式：xxx.png
                String originalFileName = multipartFile.getOriginalFilename();
                int originalFileTypeLastIndex = originalFileName != null ? originalFileName.lastIndexOf(".") : 0;
                if (originalFileTypeLastIndex == 0) {
                    return CompletableFuture.completedFuture(new BaseResult<BaseUploadResult>().fail("文件名读取失败"));
                }
                // 扩展名
                String fileNewName = UUID.randomUUID().toString(); // 123
                String originalFileType = originalFileName.substring(originalFileTypeLastIndex + 1).toLowerCase(); // png
                String fileNewFullName = MessageFormat.format("{0}.{1}", fileNewName, originalFileType); // 123.png
                String filePath = MessageFormat.format("{0}/{1}", pathStr, fileNewFullName);
                // 上传文件
                if (ProfilesActiveEnum.PRO.getActive().equals(profilesActive)) {
                    // TODO 阿里云 OSS 云存储
                    OssUtil.putObject(bucketName, endpoint, accessKeyId, accessKeySecret, multipartFile.getInputStream(), fileNewFullName);
                    filePathList.add(MessageFormat.format("/{0}", fileNewFullName));
                } else {
                    // TODO 服务器本地上传
                    filePathList.add(filePath);
                    Path path = Paths.get(filePath);
                    Files.deleteIfExists(path);
                    Files.copy(multipartFile.getInputStream(), path); // 上传到服务器 // multipartFile.transferTo( path ); // apache自带文件复制功能
                }
            }
            if (ProfilesActiveEnum.PRO.getActive().equals(profilesActive)) {
                return CompletableFuture.completedFuture(new BaseResult<BaseUploadResult>().success(new BaseUploadResult(fileUrlPrefix,filePathList)));
            }
            return CompletableFuture.completedFuture(new BaseResult<BaseUploadResult>().success(new BaseUploadResult(uploadDomain,filePathList)));
        } catch (Exception e) {
            LoggerUtil.error(MessageFormat.format("异步上传文件失败：{0}", e.getMessage()));
            return CompletableFuture.completedFuture(new BaseResult<BaseUploadResult>().fail(e.getCause().getMessage()));
        }
    }

    /**
     * @Description 文件下载
     * @Author lixing
     * @Date 2021/6/14 20:59
     */
    @Override
    public void down(BaseFileEntity baseEntity, HttpServletRequest request, HttpServletResponse response) {
        InputStream inputStream = null;
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            File downFile = new File(baseEntity.getBaseDownFileUrl());
            if (!downFile.exists()) {
                // 如果下载的文件不存在，则提供默认下载文件
                downFile = new File("C:\\Users\\lixing\\Desktop\\default.png");
                baseEntity.setBaseDownFileDelete(false);
            }
            inputStream = new FileInputStream(downFile);
            bufferedInputStream = new BufferedInputStream(inputStream);
            bufferedOutputStream = new BufferedOutputStream(response.getOutputStream());
            //
            int length = 0;
            byte[] temp = new byte[1024 * 10];
            while ((length = bufferedInputStream.read(temp)) != -1) {
                bufferedOutputStream.write(temp, 0, length);
            }
            bufferedOutputStream.flush();
            //
            if (baseEntity.isBaseDownFileDelete()) {
                FileUtil.deleteFile(downFile.getPath());
            }
        } catch (IOException e) {
            throw new BaseException("将服务器文件输出到浏览器出错：" + e.getMessage());
        } finally {
            if (bufferedOutputStream != null) {
                try {
                    bufferedOutputStream.close();
                } catch (IOException ioe) {
                    ioe.fillInStackTrace();
                }
            }
            if (bufferedInputStream != null) {
                try {
                    bufferedInputStream.close();
                } catch (IOException ioe) {
                    ioe.fillInStackTrace();
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException ioe) {
                    ioe.fillInStackTrace();
                }
            }
        }
    }
}
